﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace UndisposedLogViewer
{
    public partial class MainForm : Form
    {
        BackgroundWorker worker = new BackgroundWorker();

        int totalUndisposedObjectsCount = 0;

        Dictionary<string, List<LogEntry>> logEntriesByType = new Dictionary<string, List<LogEntry>>();
        SortedList<List<LogEntry>, string> logEntriesByCount = new SortedList<List<LogEntry>, string>(new LogEntriesByCountComparer());

        public MainForm()
        {
            InitializeComponent();

            worker.WorkerReportsProgress = true;
            worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
            worker.DoWork += new DoWorkEventHandler(worker_DoWork);
            worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
        }

        void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            ShowAllEntries();

            if (e.Error != null)
                MessageBox.Show(e.Error.ToString());
        }

        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            using (OpenFileDialog ofd = new OpenFileDialog())
            {
                if (ofd.ShowDialog() == DialogResult.OK)
                {
                    string fileName = ofd.FileName;
                    LoadLog(fileName);
                }
            }
        }

        private void LoadLog(string fileName)
        {
            worker.RunWorkerAsync(fileName);
        }

        void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            string fileName = (string)e.Argument;

            using (LogParser parser = new LogParser(fileName))
            {
                foreach (LogEntry entry in parser.GetEntries())
                {
                    var localEntry = entry;
                    UpdateDataStructures(localEntry);

                    worker.ReportProgress(0, localEntry);
                }
            }

            foreach (var dictEntry in logEntriesByType)
            {
                logEntriesByCount.Add(dictEntry.Value, dictEntry.Key);
            }
        }

        private void UpdateDataStructures(LogEntry entry)
        {
            List<LogEntry> logEntries = null;

            if (logEntriesByType.TryGetValue(entry.Type, out logEntries))
            {
                logEntries.Add(entry);
            }
            else
            {
                logEntries = new List<LogEntry>();
                logEntries.Add(entry);
                logEntriesByType.Add(entry.Type, logEntries);
            }
        }

        void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            totalUndisposedObjectsCount++;
            totalUndisposedObjectsLabel.Text = string.Format("{0} undisposed object{1} detected", totalUndisposedObjectsCount,
                totalUndisposedObjectsCount == 1 ? "" : "s");
        }

        private void ShowAllEntries()
        {
            foreach (var entry in logEntriesByCount)
            {
                var item = new ListViewItem(new string[] { entry.Key.Count.ToString(), entry.Value });
                item.Tag = entry.Value;

                objectTypesListView.Items.Add(item);
            }
        }

        class LogEntriesByCountComparer : IComparer<List<LogEntry>>
        {
            #region IComparer<List<LogEntry>> Members

            public int Compare(List<LogEntry> x, List<LogEntry> y)
            {
                if (x.Count == 0 && y.Count == 0)
                    return 0;
                // sort by descending number of log entries per type
                if (x.Count != y.Count)
                {
                    return (y.Count - x.Count);
                }
                else
                {
                    return x[0].Type.CompareTo(y[0].Type);
                }
            }

            #endregion
        }

        private void objectTypesListView_SelectedIndexChanged(object sender, EventArgs e)
        {
            var selectedItems = objectTypesListView.SelectedItems;

            if (selectedItems.Count == 0)
                return;

            var type = (string)(selectedItems[0].Tag);
            var entries = logEntriesByType[type];

            stackTraceTreeView.Nodes.Clear();

            var stackCountPairs = GetUniqueStackCountPairs(entries);

            foreach (var pair in stackCountPairs)
            {
                var rootNode = stackTraceTreeView.Nodes.Add(pair.Value.ToString() + " objects with the stack trace");

                string[] stack = pair.Key.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);

                foreach (string method in stack)
                    rootNode.Nodes.Add(method);
            }

            stackTraceTreeView.ExpandAll();
        }

        private IEnumerable<KeyValuePair<string, int>> GetUniqueStackCountPairs(List<LogEntry> entries)
        {
            var stackDictionary = new Dictionary<string, int>();

            foreach (var entry in entries)
            {
                int count = 0;

                if (stackDictionary.TryGetValue(entry.Stack, out count))
                {
                    stackDictionary[entry.Stack] = count + 1;
                }
                else
                {
                    stackDictionary.Add(entry.Stack, 1);
                }
            }

            return stackDictionary;
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            string[] args = Program.CommandLineArguments;

            if (args.Length == 0)
            {
                MessageBox.Show("No filename specified. Shutting down.");
                return;
            }
            
            string fileName = args[0];

            if (System.IO.File.Exists(fileName))
            {
                LoadLog(fileName);
            }
            else
            {
                MessageBox.Show("Could not find file : " + fileName);
            }
        }
    }
}
